#include "CPrimBox.h"

CPrimBox::CPrimBox(float width, float height, float depth):IPrimitive(),m_width(width), m_height(height), 
			m_depth(depth), m_idHitFace(0)
{
	m_vertex[0] = CVector3f(-width/2, -height/2, -depth/2);
	m_vertex[1] = CVector3f(-width/2, height/2, -depth/2);
	m_vertex[2] = CVector3f(width/2, height/2, -depth/2);
	m_vertex[3] = CVector3f(width/2, height/2, depth/2);

	m_vertex[4] = CVector3f(width/2, -height/2, depth/2);
	m_vertex[5] = CVector3f(-width/2, -height/2, depth/2);
	m_vertex[6] = CVector3f(-width/2, -height/2, -depth/2);
	m_vertex[7] = CVector3f(-width/2, height/2, -depth/2);
}

CPrimBox::~CPrimBox(){}

bool CPrimBox::intersect(const CRay& ray, float& dist, bool useGlobalCoordinates)
{
	//change the coordinate system
	CRay modRay;
	if (useGlobalCoordinates)
		globalToLocal(ray, modRay);
	else
		convertRayToLocalCoordinates(ray, modRay);

	//initialiase data
	dist = -1;
	m_idHitFace = 0;

	//for very face
	for(int i = 0; i < 6; i++)
	{
		//check intersection with a face
		float t = -1;
		bool res = intersectRayWithFace(modRay, m_vertex[i+1], m_vertex[i], m_vertex[i+2], t);

		//if no interesection, continue
		if(!res)
			continue;

		if(dist == -1 && t > 0)//first face, set automatically dist
		{
			m_idHitFace = i;
			dist = t;
		}
		else if(t < dist && t > 0)
		{
			m_idHitFace = i;
			dist = t;
		}
	}

	if(dist <= 0)
		return false;
	
	return true;
}

void CPrimBox::computeNormal(const CRay& ray, float /*dist*/, CVector3f& normal)const
{
	//get the normal
	switch(m_idHitFace)
	{
	case 0://front
		normal = CVector3f(0, 0, -1);
		break;
	case 1://top
		normal = CVector3f(0, 1, 0);
		break;
	case 2://right
		normal = CVector3f(1, 0, 0);
		break;
	case 3://back
		normal = CVector3f(0, 0, 1);
		break;
	case 4://bottom
		normal = CVector3f(0, -1, 0);
		break;
	case 5://left
		normal = CVector3f(-1, 0, 0);
		break;
	}

	//change the coordinate system
	CRay r;
	convertRayToLocalCoordinates(ray, r);

	//if we are inside an object
	if(r.getDirection().dot(normal) > 0)
		normal = normal * -1;

	//apply the wm to the normal
	convertNormalToGlobalCoordinates(normal, normal);

	
}

bool CPrimBox::isInside(const CVector3f& p)const
{
	//convert p to local coordinates
	CVector3f localP;
	convertPointToLocalCoordinates(p, localP);

	double x = m_width / 2;
	double y = m_height / 2;
	double z = m_depth / 2;
	if(localP.x >= -x && localP.x <= x && localP.y >= -y && localP.y <= y && localP.z >= -z && localP.z <= z)
		return true;

	return false;

}

bool CPrimBox::intersectRayWithFace(const CRay& r, const CVector3f& p0, const CVector3f& p1, 
		const CVector3f& p2, float& dist)const
{
	//get origin and direction of the ray
	CVector3f d = r.getDirection();
	CVector3f o = r.getOrigin();


	CVector3f w(o.x - p0.x, o.y - p0.y, o.z - p0.z);

	CVector3f u = p1 - p0;
	CVector3f v = p2 - p0;

	float D = -(u.cross(v)).dot(d);
	float a = -(w.cross(v)).dot(d) / D;
	float b = -(u.cross(w)).dot(d) / D;

	dist = (u.cross(v)).dot(w) / D;

	if(dist <= 0)
		return false;

	if(a > 0 && b > 0 && a<= 1 && b <= 1)
		return true;

	return false;
}